/*********************************************************
 *********************************************************
 ********************                  *******************
 *************                                ************
 *******                  _oo0oo_                  *******
 ***                     o8888888o                     ***
 *                       88" . "88                       *
 *                       (| -_- |)                       *
 *                       0\  =  /0                       *
 *                     ___/`---'\___                     *
 *                   .' \\|     |// '.                   *
 *                  / \\|||  :  |||// \                  *
 *                 / _||||| -:- |||||- \                 *
 *                |   | \\\  -  /// |   |                *
 *                | \_|  ''\---/''  |_/ |                *
 *                \  .-\__  '-'  ___/-. /                *
 *              ___'. .'  /--.--\  `. .'___              *
 *           ."" '<  `.___\_<|>_/___.' >' "".            *
 *          | | :  `- \`.;`\ _ /`;.`/ - ` : | |          *
 *          \  \ `_.   \_ __\ /__ _/   .-` /  /          *
 *      =====`-.____`.___ \_____/___.-`___.-'=====       *
 *                        `=---='                        *
 *      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      *
 *********__佛祖保佑__永无BUG__验收通过__钞票多多__*********
 *********************************************************/
package org.jbase.jbsc.commons.base.config.component;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import org.apache.commons.lang3.StringUtils;
import org.jbase.jbsc.commons.base.utils.MimeTypeUtils;
import org.jbase.jbsc.commons.core.dto.Base64File;
import org.jbase.jbsc.commons.core.result.FileUploadResult;
import org.jbase.jbsc.commons.core.result.ResultBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.Date;

/**
 * Project: jbsc-commons <br/>
 * File: FileUploadComponent.java <br/>
 * Class: org.jbase.jbsc.commons.base.config.component.FileUploadComponent <br/>
 * Description: <描述类的功能>. <br/>
 * Copyright: Copyright (c) 2011 <br/>
 * Company: https://gitee.com/liuzp315 <br/>
 * Makedate: 2020/9/17 上午11:58 <br/>
 *
 * @author popo
 * @version 1.0
 * @since 1.0
 */
@Component
//@PropertySource("classpath:config.yml")
public class FileUploadComponent {

    private static final Logger L = LoggerFactory.getLogger(FileUploadComponent.class);

    @Value("${image.upload.path:static/upload/}")
    private String uploadPath;

    @Value("${image.url.prefix:/upload/}")
    private String urlPrefix;

    /**
     * 上传文件
     *
     * @param file         上传的文件
     * @param hasDateDir   是否创建日期目录
     * @param relativePath 指定的目录
     * @return
     */
    public ResultBean<FileUploadResult> uploadFile(MultipartFile file, boolean hasDateDir, String relativePath) {
        ResultBean rm = ResultBean.fireFail();

        String fileName = file.getOriginalFilename(); // 文件名
        int beginIndex = fileName.lastIndexOf(".");
        String suffixName = "";
        if (beginIndex < 1) {
            String contentType = file.getContentType();
            String extension = MimeTypeUtils.getExtension(contentType);
            suffixName = "." + extension;
            if (StringUtils.isBlank(extension)) {
                suffixName = ".png"; // 后缀名
            }
        } else {
            suffixName = fileName.substring(beginIndex); // 后缀名
        }
        String filePath = dateFileName() + suffixName; // 新文件名
        if (hasDateDir) {
            String dateStr = DateUtil.format(new Date(), "yyyyMMdd");
            filePath = dateStr + "/" + filePath;// 增加日期目录
        }
        if (StringUtils.isNotBlank(relativePath)) {
            filePath = relativePath + "/" + filePath;// 增加指定目录
        }

        String path = uploadPath + filePath; // 上传后的文件含完整路径

        File destFile = new File(path); // 上传后的文件

        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }

        try {
            file.transferTo(destFile);
        } catch (IOException e) {
            e.printStackTrace();
            return rm.setMsg("上传文件异常：" + e.getMessage());
        }

        String fullUrl = urlPrefix + filePath;
        long size = file.getSize();
        String printSize = getPrintSize(size);
        FileUploadResult fur = new FileUploadResult(fileName, filePath, fullUrl, "" + size, printSize);

        return rm.success().setData(fur);
    }

    /**
     * 上传文件，会创建日期目录
     *
     * @param file 上传的文件
     * @return
     */
    public ResultBean<FileUploadResult> uploadFile(MultipartFile file) {
        return uploadFile(file, true, null);
    }

    /**
     * 上传文件，无指定目录
     *
     * @param file       上传的文件
     * @param hasDateDir 是否创建日期目录
     * @return
     */
    public ResultBean<FileUploadResult> uploadFile(MultipartFile file, boolean hasDateDir) {
        return uploadFile(file, hasDateDir, null);
    }

    /**
     * 上传文件到指定目录，不创建日期目录
     *
     * @param file         上传的文件
     * @param relativePath 指定的目录
     * @return
     */
    public ResultBean<FileUploadResult> uploadFile(MultipartFile file, String relativePath) {
        return uploadFile(file, false, relativePath);
    }

    private String dateFileName() {
        return DateUtil.format(new Date(), "yyyyMMddHHmmssSSS") + RandomUtil.randomNumbers(3);
    }

    public String getUploadPath() {
        return uploadPath;
    }

    public String getUrlPrefix() {
        return urlPrefix;
    }

    /**
     * 通过路径获取文件
     *
     * @param filePath
     * @return
     */
    public File findFileByPath(String filePath) {
        String ablatePath = uploadPath + filePath;
        File file = new File(ablatePath);
        if (file.exists() && file.isFile())
            return file;
        return null;
    }

    /**
     * 通过路径获取文件(图片)的Base64字符串
     *
     * @param filePath
     * @return
     */
    public String findBase64ByPath(String filePath) {
        String base64 = null;
        File file = findFileByPath(filePath);
        if (file == null)
            return null;

        byte[] bytes = new byte[(int) file.length()];
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            BufferedInputStream bis = new BufferedInputStream(fis);
            bis.read(bytes);
            base64 = org.apache.commons.codec.binary.Base64.encodeBase64String(bytes);
        } catch (FileNotFoundException e) {
            L.error("get img error", e);
            e.printStackTrace();
        } catch (IOException e) {
            L.error("get img error", e);
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (Exception e) {
                L.error("close getImgBase64 error", e);
            }
        }

        return base64;
    }

    /**
     * 上传Base64
     *
     * @param base64File   上传的Base64文件
     * @param hasDateDir   是否创建日期目录
     * @param relativePath 指定的目录
     * @return
     */
    public ResultBean<FileUploadResult> uploadBase64(Base64File base64File, boolean hasDateDir, String relativePath) {
        ResultBean rb = ResultBean.fireFail();

        String fileName = base64File.getName(); // 文件名
        String suffixName = base64File.getSuffix();
        if (StringUtils.isBlank(suffixName))
            suffixName = fileName.substring(fileName.lastIndexOf(".")); // 后缀名

        String filePath = dateFileName() + suffixName; // 新文件名
        if (hasDateDir) {
            String dateStr = DateUtil.format(new Date(), "yyyyMMdd");
            filePath = dateStr + "/" + filePath;// 增加日期目录
        }
        if (StringUtils.isNotBlank(relativePath)) {
            filePath = relativePath + "/" + filePath;// 增加指定目录
        }

        String path = uploadPath + filePath; // 上传后的文件含完整路径
        File destFile = new File(path); // 上传后的文件
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }

        if (base64ToFile(base64File.getBase64(), destFile)) {
            long size = base64File.getSize();
            if (size == 0)
                size = destFile.length();
            String printSize = getPrintSize(size);
            String fullUrl = urlPrefix + filePath;
            FileUploadResult fur = new FileUploadResult(fileName, filePath, fullUrl, "" + size, printSize);
            return rb.success().setData(fur);
        }
        return rb.setMsg("文件上传失败！");
    }

    private boolean base64ToFile(String base64, File file) {
        base64 = base64.substring(base64.indexOf(",") + 1);
        BufferedOutputStream bos = null;
        java.io.FileOutputStream fos = null;
        try {

            byte[] bytes = org.apache.commons.codec.binary.Base64.decodeBase64(base64);
            fos = new java.io.FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bytes);
            return true;
        } catch (Exception e) {
            L.error("base64转文件失败", e);
            return false;
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    L.error("关闭文件流bos失败", e);
                    return false;
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    L.error("关闭文件流fos失败", e);
                    return false;
                }
            }
        }
    }

    /**
     * 上传Base64，会创建日期目录
     *
     * @param file 上传的Base64文件
     * @return
     */
    public ResultBean<FileUploadResult> uploadBase64(Base64File file) {
        return uploadBase64(file, true, null);
    }

    /**
     * 上传Base64，无指定目录
     *
     * @param file       上传的Base64文件
     * @param hasDateDir 是否创建日期目录
     * @return
     */
    public ResultBean<FileUploadResult> uploadBase64(Base64File file, boolean hasDateDir) {
        return uploadBase64(file, hasDateDir, null);
    }

    /**
     * 上传Base64到指定目录，不创建日期目录
     *
     * @param file         上传的Base64文件
     * @param relativePath 指定的目录
     * @return
     */
    public ResultBean<FileUploadResult> uploadBase64(Base64File file, String relativePath) {
        return uploadBase64(file, false, relativePath);
    }


    /**
     * 文件大小转换为带单位的
     *
     * @param size
     * @return
     */
    public static String getPrintSize(long size) {
        // 如果字节数少于1024，则直接以B为单位，否则先除于1024，后3位因太少无意义
        if (size < 1024) {
            return String.valueOf(size) + "B";
        } else {
            size = size / 1024;
        }
        // 如果原字节数除于1024之后，少于1024，则可以直接以KB作为单位
        // 因为还没有到达要使用另一个单位的时候
        // 接下去以此类推
        if (size < 1024) {
            return String.valueOf(size) + "KB";
        } else {
            size = size / 1024;
        }
        if (size < 1024) {
            // 因为如果以MB为单位的话，要保留最后1位小数，
            // 因此，把此数乘以100之后再取余
            size = size * 100;
            return String.valueOf((size / 100)) + "." + String.valueOf((size % 100)) + "MB";
        } else {
            // 否则如果要以GB为单位的，先除于1024再作同样的处理
            size = size * 100 / 1024;
            return String.valueOf((size / 100)) + "." + String.valueOf((size % 100)) + "GB";
        }
    }
}
